package com.gitee.dfdiot.sdk.utils.signs;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import java.util.Map;
import java.util.TreeMap;

/**
 * API签名机制工具类
 *
 * @author HuYong
 * @date 2022/6/7
 */
public class SignUtil {

    private final static String CHARSET_UTF8 = "utf8";
    private final static String ALGORITHM = "HmacSHA1";
    private final static String SEPARATOR = "&";

    /**
     * 签名主入口
     *
     * @param method          请求HTTP方式：GET,POST,PUT,DELETE
     * @param param           请求参数 (公共参数与api业务参数)
     * @param accessKeySecret IOT平台颁发给用户的密钥ID所对应的密钥值
     * @return
     * @throws Exception
     */
    public static Map<String, String> generate(String method, Map<String, String> param, String accessKeySecret) throws Exception {
        String signString = generateSignString(method, param);
        byte[] signBytes = hmacSHA1Signature(accessKeySecret + "&", signString);
        String signature = newStringByBase64(signBytes);
        if ("POST".equalsIgnoreCase(method)) {
            param.put("signature", signature);
            return param;
        }
        param.put("signature",  URLEncoder.encode(signature, "UTF-8"));
        return param;
    }

    /**
     * 请求参数转签名字符串
     *
     * @param httpMethod
     * @param param
     * @return
     */
    private static String generateSignString(String httpMethod, Map<String, String> param) throws IOException {
        if (StringUtils.isEmpty(httpMethod)) {
            throw new RuntimeException("httpMethod can not be empty");
        }
        TreeMap<String, String> sortParam = new TreeMap<String, String>();
        sortParam.putAll(param);
        String standardQueryString = SignUrlUtil.generateQueryString(sortParam, true);

        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(httpMethod).append(SEPARATOR);
        stringBuilder.append(SignUrlUtil.percentEncode("/")).append(SEPARATOR);
        stringBuilder.append(SignUrlUtil.percentEncode(standardQueryString));
        return stringBuilder.toString();
    }

    /**
     * hmacSHA1签名
     *
     * @param secret
     * @param baseString
     * @return
     * @throws Exception
     */
    private static byte[] hmacSHA1Signature(String secret, String baseString) throws Exception {
        if (StringUtils.isEmpty(secret)) {
            throw new IOException("secret can not be empty");
        }
        if (StringUtils.isEmpty(baseString)) {
            return null;
        }
        Mac mac = Mac.getInstance("HmacSHA1");
        SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(CHARSET_UTF8), ALGORITHM);
        mac.init(keySpec);
        return mac.doFinal(baseString.getBytes(CHARSET_UTF8));
    }

    /**
     * 进行base64编码
     *
     * @param bytes
     * @return
     * @throws UnsupportedEncodingException
     */
    private static String newStringByBase64(byte[] bytes) throws UnsupportedEncodingException {
        if (null == bytes || bytes.length == 0) {
            return null;
        }
        return new String(Base64.encodeBase64(bytes), CHARSET_UTF8);
    }

}
